具体类型的问题

  一个具体类型就像通过模块定义的一个“假类型”一样,定义了一类黑盒子。一旦这种黑盒子定义好之后,它也就无法再与程序的其他部分进行实际地交互了:没有任何方式可以为了某些新的用途而调整它,除非去修改它的定义。这种情况可以认为是非常理想的,但也会导致严重缺乏灵活性。考虑在某个图形系统里定义一个类型 Shape。假定当时这个系统需要支持圆形、三角形和正方形,再假定我们已经有了

class Point{ /* ... */ };
class Color{ /* ... */ };

这里的 /**/ 分别表示一个注释的开始和结束,这种注释形式可以用于多行的注释,以及在一行结束之前结束的那种注释。

我们可能像下面这样定义形状(shape)类:

    enum Kind { circle, triangle, square };
    class Shape {
        Kind k;        // 类型域
        Point center;
        Color col;
        // ...
    public:
        void draw();
        void rotate(int);
        // ...
    };

这里的“类型域” k 是必需的,以便使 draw()rotate() 一类的函数能够确定它们正在处理的是哪种形状(在类 Pascal 的语言里,人们可以使用带有标志 k 的变体纪录)。函数 draw() 的定义可能具有下面的样子:

    void Shape::draw()
    {
        switch(k){
        case circle:
            // 画圆
            break;
        case triangle:
            // 画三角形
            break;
        case square:
            // 画正方形
            break;
        }
    }

这真是一个烂泥潭,像 draw() 这一类函数“必须知道”现存的所有形状的种类。因此,每当有一种新形状被加进系统时,这类函数中每一个的代码都需要增加。如果我们定义了一种新形状,就需要检查每个有关形状的操作并(可能)做些修改。除非我们能使用所有操作的源代码,否则我们就无法向这个系统中加进一个新形状。由于增加一个新形状涉及到“触动”在形状上的每个重要操作的代码,这样做的时候就需要极高的技术水平,也很可能将错误引进处理其他(老)形状的代码中。还有,对于特定形状的表示方式的选择也受到严重的束缚,因为它们的表示(至少是某些部分)必须塞进由通用类型 Shape 的定义所表达的常常是具有固定大小的框架之中。

🔚